home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / sbin / cups-genppdupdate < prev    next >
Text File  |  2009-09-18  |  25KB  |  875 lines

  1. #! /usr/bin/perl -w
  2. # $Id: cups-genppdupdate.in,v 1.57 2009/06/07 02:34:55 rlk Exp $
  3. # Update CUPS PPDs for Gutenprint queues.
  4. # Copyright (C) 2002-2003 Roger Leigh (rleigh@debian.org)
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2, or (at your option)
  9. # any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, write to the Free Software
  18. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19.  
  20. use strict;
  21. use Getopt::Std;
  22. use Fcntl qw(:mode);
  23. use FileHandle;
  24. use IPC::Open2;
  25.  
  26. sub parse_options ();
  27. sub get_driver_version ();
  28. sub update_ppd ($); # Original PPD filename
  29. sub get_ppd_fh ($$$$$); # Return contents of desired PPD
  30. sub find_ppd ($$$$); # Gutenprint Filename, driver, language (e.g. en, sv),
  31.              # region (e.g. GB, DE)
  32. sub get_ppd_data (*$$$$$); # Source PPD FH
  33.  
  34. our $opt_d; # Debug mode
  35. our $opt_h; # Help
  36. our $opt_n; # No action
  37. our $opt_q; # Quiet mode
  38. our $opt_s; # Source PPD location
  39. our $opt_p; # New PPD location
  40. our $opt_P; # PPD generator location
  41. our $opt_v; # Verbose mode
  42. our $opt_N; # Don't update PPD file options
  43. our $opt_o; # Output directory
  44. our $opt_r; # Gutenprint version
  45. our $opt_i; # Interactive
  46. our $opt_f; # Force upgrade
  47. our $opt_l; # Language
  48.  
  49. my $debug = 0;
  50. my $verbose = 0;   # Verbose output
  51. my $interactive = 0;
  52. my $quiet = 0;     # No output
  53. my $no_action = 0; # Don't output files
  54. my $reset_defaults = 0;        # Reset options to default settings
  55. my $version = "5.2";
  56. my $micro_version = "5.2.4";
  57. my $use_static_ppd = "no";
  58. my $file_version = '"5.2.4"$';
  59.  
  60. my $ppd_dir = "/etc/cups/ppd"; # Location of in-use CUPS PPDs
  61. my $ppd_root_dir = "/usr/share/ppd";
  62. my $ppd_base_dir = "$ppd_root_dir/gutenprint/$version"; # Available PPDs
  63. my $ppd_out_dir = "";        # By default output into source directory
  64. my $gzext = ".gz";
  65. my $updated_ppd_count = 0;
  66. my $skipped_ppd_count = 0;
  67. my $failed_ppd_count = 0;
  68. my $exit_after_parse_args = 0;
  69. my @languages = qw(Global C cs da de el en_GB es fr hu it ja nb nl pl pt ru sk sv zh_TW);
  70.  
  71. my $serverdir = "/usr/lib/cups";
  72. my $driver_bin = "$serverdir/driver/gutenprint.$version";
  73. my $driver_version = "";
  74. my $server_multicat = 0;
  75. my $server_multicat_initialized = 0;
  76.  
  77. if (-x $driver_bin) {
  78.     get_driver_version();
  79. }
  80.  
  81. $Getopt::Std::STANDARD_HELP_VERSION = 1;
  82.  
  83. my @ppd_files; # A list of in-use Gutenprint PPD files
  84.  
  85. # Used to convert a language name to its two letter code
  86. my %languagemappings = (
  87.             "chinese"    => "cn",
  88.             "danish"     => "da",
  89.             "dutch"      => "nl",
  90.             "english"    => "en",
  91.             "finnish"    => "fi",
  92.             "french"     => "fr",
  93.             "german"     => "de",
  94.             "greek"      => "el",
  95.             "hungarian"  => "hu",
  96.             "italian"    => "it",
  97.             "japanese"   => "jp",
  98.             "norwegian"  => "no",
  99.             "polish"     => "pl",
  100.             "portuguese" => "pt",
  101.             "russian"    => "ru",
  102.             "slovak"     => "sk",
  103.             "spanish"    => "es",
  104.             "swedish"    => "sv",
  105.             "turkish"    => "tr"
  106. );
  107.  
  108.  
  109. # Check command-line options...
  110.  
  111. parse_options();
  112.  
  113.  
  114. # Set a secure umask...
  115.  
  116. umask 0177;
  117.  
  118.  
  119. # Find all in-use Gutenprint PPD files...
  120. # For case-insensitive filesystems, use only one of .ppd and .PPD
  121. # (bug 1929738).
  122.  
  123. if (@ARGV) {
  124.     my $f;
  125.     foreach $f (@ARGV) {
  126.     if (-f $f and ($f =~ /\.ppd$/i or $f =~ /\//)) {
  127.         if (-f $f) {
  128.         push @ppd_files, $f;
  129.         } else {
  130.         print STDERR "Cannot find file $f\n";
  131.         }
  132.     } elsif (-f "$ppd_dir/$f" or
  133.          -f "$ppd_dir/$f.ppd" or
  134.          -f "$ppd_dir/$f.PPD") {
  135.         if (-f "$ppd_dir/$f") {
  136.         push @ppd_files, "$ppd_dir/$f";
  137.         } elsif (-f "$ppd_dir/$f.ppd") {
  138.         push @ppd_files, "$ppd_dir/$f.ppd";
  139.         } elsif (-f "$ppd_dir/$f.PPD") {
  140.         push @ppd_files, "$ppd_dir/$f.PPD";
  141.         }
  142.     }  else {
  143.         print STDERR "Cannot find file $ppd_dir/$f, $ppd_dir/$f.ppd, or $ppd_dir/$f.PPD\n";
  144.     }
  145.     }
  146. } else {
  147.     my @ppdtmp = glob("$ppd_dir/*.{ppd,PPD}");
  148.     my (%ppd_map);
  149.     map { $ppd_map{$_} = 1 } @ppd_files;
  150.     foreach my $f (@ppdtmp) {
  151.     if ($f =~ /\.PPD$/) {
  152.         my ($g) = $f;
  153.         $g =~ s/\.PPD$/.ppd/;
  154.         if (! $ppd_map{$g}) {
  155.         push @ppd_files, $f;
  156.         }
  157.     } else {
  158.         push @ppd_files, $f;
  159.     }
  160.     }
  161. }
  162.  
  163. # Update each of the Gutenprint PPDs, where possible...
  164.  
  165. foreach (@ppd_files) {
  166.     my ($status) = update_ppd($_);
  167.     last if ($status == -2);
  168.     $failed_ppd_count++ if ($status == 0);
  169.     $updated_ppd_count++ if ($status == 1);
  170.     $skipped_ppd_count++ if ($status == -1);
  171. }
  172.  
  173. if (!$quiet || $verbose) {
  174.     if (!@ppd_files) {
  175.     print STDOUT "No Gutenprint PPD files to update.\n";
  176.     } elsif ($updated_ppd_count > 0) {
  177.     my $plural = $updated_ppd_count == 1 ? "" : "s";
  178.     print STDOUT "Updated $updated_ppd_count PPD file${plural}.";
  179.     if (!defined $opt_o || $opt_o ne "") {
  180.         print STDOUT "Restart cupsd for the changes to take effect.";
  181.     }
  182.     print STDOUT "\n";
  183.     } else {
  184.     if ($failed_ppd_count > 0) {
  185.         print STDOUT "Failed to update any PPD files\n";
  186.     } else {
  187.         print STDOUT "Did not update any PPD files\n";
  188.     }
  189.     }
  190. }
  191. exit ($failed_ppd_count > 0);
  192.  
  193. sub HELP_MESSAGE($;$$$) {
  194.     my ($fh) = @_;
  195.     print $fh "Usage: $0 [OPTION]... [PPD_FILE]...\n";
  196.     print $fh "Update CUPS+Gutenprint PPD files.\n\n";
  197.     print $fh "  -d flags    Enable debugging\n";
  198.     print $fh "  -h          Display this help text\n";
  199.     print $fh "  -n          No-action.  Don't overwrite any PPD files.\n";
  200.     print $fh "  -q          Quiet mode.  No messages except errors.\n";
  201.     print $fh "  -s ppd_dir  Use ppd_dir as the source PPD directory.\n";
  202.     print $fh "  -p ppd_dir  Update PPD files in ppd_dir.\n";
  203.     print $fh "  -P driver   Use the specified driver binary to generate PPD files.\n";
  204.     print $fh "  -v          Verbose messages.\n";
  205.     print $fh "  -N          Reset options to defaults.\n";
  206.     print $fh "  -o out_dir  Output PPD files to out_dir.\n";
  207.     print $fh "  -r version  Use PPD files for Gutenprint major.minor version.\n";
  208.     print $fh "  -f          Ignore new PPD file safety checks.\n";
  209.     print $fh "  -i          Prompt (interactively) for each PPD file.\n";
  210.     print $fh "  -l language Language choice (Gutenprint 5.1 or below).\n";
  211.     print $fh "              Choices: " . join(" ", @languages) . "\n";
  212.     print $fh "              Or -loriginal to preserve original language\n";
  213.     print $fh "                 with Gutenprint 5.2 or above\n";
  214.     exit(0);
  215. }
  216.  
  217. # Getopt::Std calls VERSION_MESSAGE followed by HELP_MESSAGE if --help
  218. # is passed.  If --version is passed, it calls only VERSION_MESSAGE.
  219. # So we have to make sure to exit, but we want to allow --help to
  220. # print out the help message.
  221. sub VERSION_MESSAGE($;$$$) {
  222.     my ($fh) = @_;
  223.     print "cups-genppdupdate from Gutenprint $micro_version\n";
  224.     $exit_after_parse_args = 1;
  225. }
  226.  
  227. sub help() {
  228.     HELP_MESSAGE(\*STDOUT);
  229. }
  230.           
  231. sub check_multicat() {
  232. }
  233.  
  234. sub get_driver_version() {
  235.     open(DBIN, "$driver_bin org.gutenprint.extensions 2>/dev/null |") or return 0;
  236.     my ($line);
  237.     $server_multicat = 0;
  238.     while ($line = <DBIN>) {
  239.     if ($line =~ /^org.gutenprint.multicat$/) {
  240.         $server_multicat = 1;
  241.         last;
  242.     }
  243.     }
  244.     close DBIN;
  245.     $driver_version = `$driver_bin VERSION`;
  246.     chomp $driver_version;
  247. }
  248.  
  249. sub parse_options () {
  250.     if (!getopts('d:hnqs:vNo:p:P:r:ifl:')) {
  251.     help();
  252.     }
  253.     if ($opt_n) {
  254.     $no_action = 1;
  255.     }
  256.     if ($opt_d) {
  257.     $debug = $opt_d;
  258.     }
  259.     if ($opt_v) {
  260.     $verbose = 1;
  261.     $quiet = 0;
  262.     }
  263.     if ($opt_q) {
  264.     $verbose = 0;
  265.     $quiet = 1;
  266.     }
  267.     if ($opt_N) {
  268.     $reset_defaults = 1;
  269.     }
  270.     if ($opt_o) {
  271.     if (-d $opt_o) {
  272.         $ppd_out_dir = "$opt_o";
  273.     }
  274.     else {
  275.         die "$opt_o: invalid directory: $!\n";
  276.     }
  277.     }
  278.     if ($opt_r) {
  279.     if ($version ne $opt_r) {
  280.         $version = $opt_r;
  281.         if ($opt_s) {
  282.         if (-d $opt_s) {
  283.             $ppd_base_dir = "$opt_s";
  284.             $driver_bin = "";
  285.             $server_multicat = 0;
  286.             $use_static_ppd = "yes";
  287.         } else {
  288.             die "$opt_s: invalid directory: $!\n";
  289.         }
  290.         } else {
  291.         $ppd_base_dir = "$ppd_root_dir/gutenprint/$version";
  292.         $driver_bin = "$serverdir/driver/gutenprint.$version";
  293.         }
  294.         $driver_version = "";
  295.         # If user specifies version, we're not going to be able to check
  296.         # for an exact match.
  297.         $file_version = "\"$version";
  298.         if (-x $driver_bin) {
  299.         get_driver_version();
  300.         $use_static_ppd = "no";
  301.         $file_version = "\"$driver_version\"\$";
  302.         } elsif (! -d $ppd_base_dir && ! -l $ppd_base_dir) {
  303.         die "Gutenprint $version does not appear to be installed!\n";
  304.         }
  305.     }
  306.     }
  307.     if ($opt_s) {
  308.     if (-d $opt_s) {
  309.         $ppd_base_dir = "$opt_s";
  310.         $driver_bin = "";
  311.         $server_multicat = 0;
  312.         $driver_version = "";
  313.         $use_static_ppd = "yes";
  314.     }
  315.     else {
  316.         die "$opt_s: invalid directory: $!\n";
  317.     }
  318.     }
  319.     if ($opt_p) {
  320.     if (-d $opt_p) {
  321.         $ppd_dir = "$opt_p";
  322.     }
  323.     else {
  324.         die "$opt_p: invalid directory: $!\n";
  325.     }
  326.     }
  327.     if ($opt_P) {
  328.     if (-x $opt_P) {
  329.         $driver_bin = "$opt_P";
  330.         get_driver_version();
  331.         $use_static_ppd = "no";
  332.     }
  333.     else {
  334.         die "$opt_P: invalid executable: $!\n";
  335.     }
  336.     }
  337.     if ($opt_h) {
  338.     help();
  339.     }
  340.     if ($opt_l && lc $opt_l ne "original" && ! grep { $_ eq $opt_l } @languages) {
  341.     print STDERR "Unknown language '$opt_l'\n";
  342.     help();
  343.     }
  344.     if ($opt_i) {
  345.     $interactive = 1;
  346.     }
  347.     if ($exit_after_parse_args) {
  348.     exit(0);
  349.     }
  350.     if ($verbose && $driver_version ne "") {
  351.     print STDOUT "Updating PPD files from Gutenprint $driver_version\n";
  352.     }
  353. }
  354.  
  355. sub get_ppd_fh($$$$$) {
  356.     my ($ppd_source_filename, $filename, $driver, $locale, $region) = @_;
  357.  
  358.     my $source_data;
  359.     my ($new_ppd_filename);
  360.  
  361.     if ($use_static_ppd eq "no" && $driver_version ne "") {
  362.     my ($simplified);
  363.     if ($filename =~ m,.*/([^/]*)(.sim)(.ppd)?(.gz)?$,) {
  364.         $simplified = "simple";
  365.     } else {
  366.         $simplified = "expert";
  367.     }
  368.     my ($url);
  369.     my (@url_list);
  370.     if (((defined $opt_r && $opt_r < 5.2) ||
  371.          (defined $opt_l && $opt_l ne "")) &&
  372.         $locale ne "") {
  373.         if ($region) {
  374.         push @url_list, "gutenprint.$version://$driver/$simplified/${locale}_${region}";
  375.         }
  376.         push @url_list, "gutenprint.$version://$driver/$simplified/${locale}";
  377.     }
  378.     push @url_list, "gutenprint.$version://$driver/$simplified";
  379.     foreach $url (@url_list) {
  380.         $new_ppd_filename = $url;
  381.         if ($debug & 8) {
  382.         print "Trying ", (! $server_multicat ? "$driver_bin cat " : ""), "$url for $driver, $simplified, $locale, $region\n";
  383.         }
  384.         if ($server_multicat) {
  385.         if (! $server_multicat_initialized) {
  386.             my ($pid) = open2(*Reader, *Writer, "$driver_bin org.gutenprint.multicat");
  387.             $server_multicat_initialized = 1;
  388.         }
  389.         print Writer "$url\n";
  390.         return ($new_ppd_filename, \*Reader);
  391.         }
  392.         if (open PPD, "$driver_bin cat $url |") {
  393.         return ($new_ppd_filename, \*PPD);
  394.         }
  395.     }
  396.     # Otherwise fall through and try to find a static PPD
  397.     }
  398.  
  399.     # Search for a PPD matching our criteria...
  400.  
  401.     $new_ppd_filename = find_ppd($filename, $driver, $locale, $region);
  402.     if (!defined($new_ppd_filename)) {
  403.         # There wasn't a valid source PPD file, so give up.
  404.         print STDERR "$ppd_source_filename: no valid candidate for replacement.  Skipping\n";
  405.         print STDERR "$ppd_source_filename: please upgrade this PPD manually\n";
  406.     return ("", undef);
  407.     }
  408.     if ($debug & 1) {
  409.     print "Candidate PPD: $new_ppd_filename\n";
  410.     }
  411.  
  412.     my $suffix = "\\" . $gzext; # Add '\', so m// matches the '.'.
  413.     if ($new_ppd_filename =~ m/.gz$/) { # Decompress input buffer
  414.     open GZIN, "gunzip -c $new_ppd_filename |"
  415.         or die "$_: can't open for decompression: $!";
  416.     return ($new_ppd_filename, \*GZIN);
  417.     } else {
  418.     open SOURCE, $new_ppd_filename
  419.         or die "$new_ppd_filename: can't open source file: $!";
  420.     binmode SOURCE;
  421.     return ($new_ppd_filename, \*SOURCE);
  422.     }
  423. }
  424.  
  425. # Update the named PPD file.
  426. sub update_ppd ($) {
  427.     my $ppd_source_filename = $_;
  428.     my $ppd_dest_filename = $ppd_source_filename;
  429.     if ($ppd_out_dir) {
  430.     $ppd_dest_filename =~ s;(.*)/([^/]+);$2;;
  431.     $ppd_dest_filename = "$ppd_out_dir/$ppd_dest_filename";
  432.     }
  433.  
  434.     open ORIG, $_ or die "$_: can't open PPD file: $!";
  435.     seek (ORIG, 0, 0) or die "can't seek to start of PPD file: $!";
  436.     my @orig_metadata = stat(ORIG);
  437.     if ($debug & 1) {
  438.     print "Source Filename: $ppd_source_filename\n";
  439.     }
  440.     my ($filename) = "";
  441.     my ($driver) = "";
  442.     my ($gutenprintdriver) = "";
  443.     my ($locale) = "";
  444.     my ($lingo) = "";
  445.     my ($region) = "";
  446.     my ($valid) = 0;
  447.     my ($orig_locale) = "";
  448.     while (<ORIG>) {
  449.     if (/\*StpLocale:/) {
  450.         ($locale) = m/^\*StpLocale:\s*\"(.*)\"$/;
  451.         $orig_locale = $locale;
  452.         $valid = 1;
  453.     } elsif (/^\*LanguageVersion/) {
  454.         ($lingo) = m/^\*LanguageVersion:\s*(.*)$/;
  455.     } elsif (/^\*StpDriverName:/ ) {
  456.         ($driver) = m/^\*StpDriverName:\s*\"(.*)\"$/;
  457.         $valid = 1;
  458.     } elsif (/\*%End of / && $driver eq "") {
  459.         ($driver) = m/^\*%End of\s*(.*).ppd$/;
  460.     } elsif (/^\*StpPPDLocation:/ ) {
  461.         ($filename) = m/^\*StpPPDLocation:\s*\"(.*)\"$/;
  462.         $valid = 1;
  463.     } elsif (/^\*%Gutenprint Filename:/) {
  464.         $valid = 1;
  465.     }
  466.     if ($filename and $driver and $lingo and $locale) {
  467.         last;
  468.     }
  469.     if (! $valid && /^\*OpenUI/) {
  470.         last;
  471.     }
  472.     }
  473.     if (! $valid) {
  474. #    print STDERR "Skipping $ppd_source_filename: not a Gutenprint PPD file\n";
  475.     return -1;
  476.     }
  477.     if (defined $opt_l && $opt_l ne "" && lc $opt_l ne "original") {
  478.     $locale = $opt_l;
  479.     $orig_locale = $locale;
  480.     }
  481.     if ($debug & 2) {
  482.     print "Gutenprint Filename: $filename\n";
  483.     if ($opt_l) {
  484.         print "Locale: $locale (from -l)\n";
  485.     } else {
  486.         print "Locale: $locale\n";
  487.     }
  488.     print "Language: $lingo\n";
  489.     print "Driver: $driver\n";
  490.     }
  491.     if ($locale) {
  492.     # Split into the language and territory.
  493.     ($locale, $region) = split(/_/, $locale);
  494.     } else {
  495.     # Split into the language and territory.
  496.     ($locale, $region) = split(/_/, $lingo);
  497.     # Convert language into language code.
  498.     $locale = $languagemappings{"\L$lingo"};
  499.     if (!defined($locale)) {
  500.         $locale = "C"; # Fallback if there isn't one.
  501.     }
  502.     }
  503.     if (! defined($region)) {
  504.     $region = "";
  505.     }
  506.     if ($debug & 2) {
  507.     print "Base Locale: $locale\n";
  508.     print "Region: $region\n";
  509.     }
  510.  
  511.     # Read in the new PPD, decompressing it if needed...
  512.  
  513.     my ($new_ppd_filename, $source_fd) =
  514.     get_ppd_fh($ppd_source_filename, $filename, $driver, $locale, $region);
  515.  
  516.     if (! defined $source_fd) {
  517.     print "Unable to retrieve PPD file!\n";
  518.     return 0;
  519.     }
  520.  
  521.     if ($interactive) {
  522.     print "Update PPD $ppd_source_filename from $new_ppd_filename [nyq]? ";
  523.     my $input = readline(*STDIN);
  524.     if ($input =~ /^q/i) {
  525.         close $source_fd if !$server_multicat;
  526.         print "Skipping all...\n";
  527.         return -2;
  528.     } elsif (! ($input =~ /^y/i)) {
  529.         close $source_fd if !$server_multicat;
  530.         print "Skipping...\n";
  531.         return -1;
  532.     }
  533.     }
  534.  
  535.     # Extract the default values from the original PPD...
  536.  
  537.     seek(ORIG, 0, 0);
  538.  
  539.     my ($odt, $oopt, $ores, $odef) = get_ppd_data(ORIG, 1, 0, 1, 1, 0);
  540.     my ($ndt, $nopt, $nres, $ndef, $source_data) = get_ppd_data($source_fd, 1, 1, 1, 1, 1);
  541.  
  542.     # Close original and temporary files...
  543.  
  544.     close ORIG;
  545.     if (! $server_multicat && ! close $source_fd) {
  546.     print "Unable to retrieve new PPD file: $!\n";
  547.     return 0;
  548.     }
  549.  
  550.     my %orig_default_types = %$odt;
  551.     my %new_default_types = %$ndt;
  552.     my %defaults = %$odef;
  553.     my %new_defaults = %$ndef;
  554.     my %options = %$nopt;
  555.     my %resolution_map = %$nres;
  556.     my %old_resolution_map = reverse %$ores;
  557.  
  558.     # Store previous language in the PPD file so that -l original works
  559.     # correctly.
  560.  
  561.     if ($orig_locale ne "") {
  562.     $source_data =~ s/(\*StpLocale:\s*\")(.*)(\")/$1$orig_locale$3/;
  563.     }
  564.  
  565.     if ($debug & 4) {
  566.     print "Options (Old->New Default Type):\n";
  567.     foreach (sort keys %options) {
  568.         my ($old_type) = $orig_default_types{$_};
  569.         my ($new_type) = $new_default_types{$_};
  570.         if (! defined($old_type)) {
  571.         $old_type = '(New)';
  572.         }
  573.         if ($old_type ne $new_type) {
  574.         print "  $_ ($old_type -> $new_type) :  ";
  575.         } else {
  576.         print "  $_ ($new_type) :  ";
  577.         }
  578.         my ($def) = $defaults{"Default$_"};
  579.         foreach my $opt (@{$options{$_}}) {
  580.         if (defined $def && $def eq $opt) {
  581.             print "*";
  582.         }
  583.         print "$opt ";
  584.         }
  585.         print "\n";
  586.     }
  587.     if (keys %resolution_map) {
  588.         print "Resolution Map:\n";
  589.         foreach (sort keys %resolution_map) {
  590.         print "   $_: $resolution_map{$_}\n";
  591.         }
  592.     }
  593.     if (keys %old_resolution_map) {
  594.         print "Old Resolution Map:\n";
  595.         foreach (sort keys %old_resolution_map) {
  596.         print "   $_: $old_resolution_map{$_}\n";
  597.         }
  598.     }
  599.     print "Non-UI Defaults:\n";
  600.     foreach (sort keys %defaults) {
  601.         my ($xkey) = $_;
  602.         $xkey =~ s/^Default//;
  603.         if (! defined ($options{$xkey})) {
  604.         print "  $_: $defaults{$_}\n";
  605.         }
  606.     }
  607.     print "Default Types of dropped options:\n";
  608.     foreach (sort keys %orig_default_types) {
  609.         if (! defined($options{$_})) {
  610.         print "  $_: $orig_default_types{$_}\n";
  611.         }
  612.     }
  613.     }
  614.  
  615.     if ($no_action) {
  616.     if (!$quiet || $verbose) {
  617.         if ($ppd_dest_filename eq $ppd_source_filename) {
  618.         print STDOUT "Would update $ppd_source_filename using $new_ppd_filename\n";
  619.         } else {
  620.         print STDOUT "Would update $ppd_source_filename to $ppd_dest_filename using $new_ppd_filename\n";
  621.         }
  622.     }
  623.     return 0;
  624.     }
  625.  
  626.     if  (! $reset_defaults) {
  627.     # Update source buffer with old defaults...
  628.  
  629.     # Loop through each default in turn.
  630. default_loop:
  631.     foreach my $default_option (sort keys %defaults) {
  632.         my $option;
  633.         my $default_option_value = $defaults{$default_option};
  634.         ($option = $default_option) =~ s/Default//; # Strip off `Default'
  635.         # Check method is valid
  636.         my $orig_method = $orig_default_types{$option};
  637.         my $new_method = $new_default_types{$option};
  638.         my $new_default = $new_defaults{$default_option};
  639.         if ((!defined($orig_method) || !defined($new_method)) ||
  640.         $orig_method ne $new_method) {
  641.         next;
  642.         }
  643.         if (defined($new_default) &&
  644.         $default_option_value eq $new_default) {
  645.         if ($verbose) {
  646.             print "$ppd_source_filename: Preserve *$default_option ($default_option_value)\n";
  647.         }
  648.         next;
  649.         }
  650.         if ($new_method eq "PickOne") {
  651.         # Check the old setting is valid
  652.         foreach my $opt (@{$options{$option}}) {
  653.             my $def_option = $default_option_value;
  654.             my $odef_option = $def_option;
  655.             if ($option eq "Resolution" &&
  656.             defined $old_resolution_map{$def_option}) {
  657.             if ($debug & 4) {
  658.                 print "Intermapping old resolution $def_option to $old_resolution_map{$def_option}\n";
  659.             }
  660.             $def_option = $old_resolution_map{$def_option};
  661.             }
  662.             my @dopts = ($def_option);
  663.             if ($def_option ne $odef_option) {
  664.             push @dopts, $odef_option;
  665.             }
  666.  
  667.             foreach my $dopt (@dopts) {
  668.             if (($dopt eq $opt) ||
  669.                 ($option eq "Resolution" &&
  670.                  (defined $resolution_map{$dopt}) &&
  671.                  ($dopt = $resolution_map{$dopt}) eq $opt)) { # Valid option
  672.                 # Set the option in the new PPD
  673.                 $source_data =~ s/^\*($default_option).*/*$1:$dopt/m;
  674.                 if ($verbose) {
  675.                 print "$ppd_source_filename: Set *$default_option to $dopt\n";
  676.                 }
  677.                 next default_loop;
  678.             }
  679.             }
  680.         }
  681.         warn "Warning: $ppd_source_filename: Invalid option: *$default_option: $defaults{$default_option}.  Using default setting $new_defaults{$default_option}.\n";
  682.         next;
  683.         }
  684.         warn "Warning: $ppd_source_filename: PPD OpenUI method $new_default_types{$default_option} not understood.\n";
  685.     }
  686.     }
  687.  
  688.     # Write new PPD...
  689.  
  690.     my $tmpnew = "${ppd_dest_filename}.new";
  691.     if (! open NEWPPD, "> $tmpnew") {
  692.     warn "Can't create $tmpnew: $!\n";
  693.     return 0;
  694.     }
  695.     print NEWPPD $source_data;
  696.     if (! close NEWPPD) {
  697.     warn "Can't write to $tmpnew: $!\n";
  698.     unlink $tmpnew;
  699.     return 0;
  700.     }
  701.  
  702.     if (! rename $tmpnew, $ppd_dest_filename) {
  703.     warn "Can't rename $tmpnew to $ppd_dest_filename: $!\n";
  704.     unlink $tmpnew;
  705.     return 0;
  706.     }
  707.     chown($orig_metadata[4], $orig_metadata[5], $ppd_dest_filename);
  708.     chmod(($orig_metadata[2] & 0777), $ppd_dest_filename);
  709.  
  710.     if (!$quiet || $verbose) {
  711.     if ($ppd_dest_filename eq $ppd_source_filename) {
  712.         print STDOUT "Updated $ppd_source_filename using $new_ppd_filename\n";
  713.     } else {
  714.         print STDOUT "Updated $ppd_source_filename to $ppd_dest_filename using $new_ppd_filename\n";
  715.     }
  716.     }
  717.     return 1;
  718.     # All done!
  719. }
  720.  
  721. # Find a suitable source PPD file
  722. sub find_ppd ($$$$) {
  723.     my($gutenprintfilename, $drivername, $lang, $region) = @_;
  724.     my $file; # filename to return
  725.     my ($key) = '^\\*FileVersion:[     ]*' . "$file_version";
  726.     my ($lingo, $suffix, $base, $basedir);
  727.     my ($current_best_file, $current_best_time);
  728.     my ($stored_name, $stored_dir, $simplified);
  729.     $stored_name = $gutenprintfilename;
  730.     $stored_name =~ s,.*/([^/]+\.[0-9]+\.[0-9]+)(\.sim)?(\.ppd)?(\.gz)?$,$1,;
  731.     if ($gutenprintfilename =~ m,.*/([^/]*)(\.sim)(\.ppd)?(\.gz)?$,) {
  732.     $simplified = ".sim";
  733.     } else {
  734.     $simplified = "";
  735.     }
  736.     $stored_dir = $gutenprintfilename;
  737.     $stored_dir =~ s,(.*)/([^/]*)$,$1,;
  738.  
  739.     $current_best_file = "";
  740.     $current_best_time = 0;
  741.     my (@basedirs);
  742.     if ($opt_s) {
  743.     @basedirs = ($opt_s);
  744.     } else {
  745.     @basedirs = ($ppd_base_dir, $stored_dir, $ppd_root_dir);
  746.     }
  747.  
  748.     my (@lingos);
  749.     if ($region ne "") {
  750.     push @lingos, "${lang}_${region}/";
  751.     }
  752.     push @lingos, "$lang/";
  753.     if ($lang ne "C") {
  754.     push @lingos, "C/";
  755.     }
  756.     push @lingos, "en/", "";
  757.     push @lingos, "Global/";
  758.     my (@bases);
  759.     push @bases, "stp-${drivername}.$version${simplified}";
  760.     push @bases, "${drivername}.$version${simplified}";
  761.     if ($stored_name ne "${drivername}.$version${simplified}" and
  762.     $stored_name ne "stp-${drivername}.$version${simplified}") {
  763.     push @bases, $stored_name;
  764.     }
  765.     push @bases, $drivername;
  766.  
  767.     # All possible candidates, in order of usefulness and gzippedness
  768.     foreach $lingo (@lingos) {
  769.     foreach $suffix (".ppd$gzext",
  770.              ".ppd") {
  771.         foreach $base (@bases) {
  772.         foreach $basedir (@basedirs) {
  773.                     if ($basedir eq "" || $base eq "") { next; }
  774.             my ($fn) = "$basedir/$lingo$base$suffix";
  775.             if ($debug & 8) {
  776.                         print "Trying $fn for $gutenprintfilename, $lang, $region\n";
  777.                     }
  778. # Check that it is a regular file, owned by root.root, not writable
  779. # by other, and is readable by root.  i.e. the file is secure.
  780.             my @sb = stat $fn or next;
  781.             if ($debug & 8) {
  782.                         print "  Candidate $fn for $gutenprintfilename, $lang, $region\n";
  783.                     }
  784.             if ($opt_f || (S_ISREG($sb[2]) && ($sb[4] == 0))) {
  785.             # Check that the file is a valid Gutenprint PPD file
  786.             # of the correct version.
  787.             my $new_file_version;
  788.             if ($fn =~ m/\.gz$/) {
  789.                 $new_file_version = `gunzip -c $fn | grep '$key'`;
  790.             } else {
  791.                 $new_file_version = `cat $fn | grep '$key'`;
  792.             }
  793.             if ($new_file_version ne "") {
  794.                             if ($debug & 8) {
  795.                     print "   Format valid: time $sb[9] best $current_best_time prev $current_best_file cur $fn!\n";
  796.                 }
  797.                 if ($sb[9] > $current_best_time) {
  798.                 $current_best_time = $sb[9];
  799.                 $current_best_file = $fn;
  800.                         if ($debug & 8) {
  801.                                     print STDERR "***current_best_file is $fn\n";
  802.                                 }
  803.                 }
  804.             } elsif ($debug & 8) {
  805.                 print "   Format invalid\n";
  806.             }
  807.             }
  808.             else {
  809.             $_ = $fn;
  810.             if (! -d $fn && ! /\/$/) {
  811.                 print STDERR "$fn: not a regular file, or insecure ownership and permissions.  Skipped\n";
  812.             }
  813.             }
  814.         }
  815.         }
  816.     }
  817.     }
  818.     if ($current_best_file) {
  819.         return $current_best_file;
  820.     }
  821. # Yikes!  Cannot find a valid PPD file!
  822.     return undef;
  823. }
  824.  
  825. # Return default type, options, resolutions, and default values.
  826. # More efficient since it takes only one pass over the data.
  827. sub get_ppd_data(*$$$$$) {
  828.     my ($fh, $types, $opts, $resolutions, $defaults, $data) = @_;
  829.     my (%options, %defaults, %resolution_map, %default_types);
  830.     my $cur_opt = "";
  831.     my (@optionlist);
  832.     my ($source_data) = "";
  833.     if ($reset_defaults) {
  834.     $types = 0;
  835.     $opts = 0;
  836.     $resolutions = 0;
  837.     $defaults = 0;
  838.     }
  839.  
  840.     if ($resolutions || $types || $opts || $defaults || $data) {
  841.     while (<$fh>) {
  842.         last if $_ eq "*%*%EOFEOF\n";
  843.         $source_data .= $_ if ($data);
  844.         chomp;
  845.         if (($types || $opts) && m/^\*OpenUI/) {
  846.         my ($key, $value) = /^\*OpenUI\s\*([[:alnum:]]+).*:\s([[:alnum:]]+)/;
  847.         if ($key && $value) {
  848.             $default_types{$key}=$value;
  849.             $cur_opt = $key;
  850.         }
  851.         } elsif ($opts && m/^\*CloseUI/) {
  852.         if ($cur_opt ne "") {
  853.             $options{$cur_opt} = [ @optionlist ];
  854.             $cur_opt = "";
  855.         }
  856.         @optionlist = ();
  857.         } elsif ($opts && m/^\*$cur_opt/) {
  858.         my ($value) = /^\*$cur_opt\s*([[:alnum:]]+)[\/:]/;
  859.         if (defined $value && $value) {
  860.             push @optionlist, $value;
  861.         }
  862.         } elsif ($resolutions && m/^\*StpResolutionMap:/) {
  863.         my ($junk, $new, $old) = split;
  864.         $resolution_map{$old} = $new;
  865.         } elsif ($defaults && m/^\*Default/) {
  866.         my($key, $value) = /^\*([[:alnum:]]+):\s*([[:alnum:]]+)/;
  867.         if ($key && $value) {
  868.             $defaults{$key}=$value;
  869.         }
  870.         }
  871.     }
  872.     }
  873.     return (\%default_types, \%options, \%resolution_map, \%defaults, $source_data);
  874. }
  875.